// pkg/console/terminal.go
package console

import (
	"context"
	"fmt"
	"io"
	"os"
	"sync"
	"time"

	"github.com/sirupsen/logrus"
	"golang.org/x/term"
	"libvirt.org/go/libvirt"
)

// TerminalConsole 终端控制台
type TerminalConsole struct {
	conn   *libvirt.Connect
	domain *libvirt.Domain
	stream *libvirt.Stream
	logger *logrus.Logger

	// 终端状态
	oldState *term.State
	isRaw    bool

	// 控制通道
	stopCh chan struct{}
	errCh  chan error

	// 同步
	mu      sync.Mutex
	running bool

	// 配置
	config *ConsoleConfig
}

// ConsoleConfig 控制台配置
type ConsoleConfig struct {
	DeviceName string        // 设备名称，如 "serial0", "console0"
	Escape     rune          // 退出快捷键，默认 Ctrl+]
	LogFile    string        // 日志文件路径
	Reconnect  bool          // 断线重连
	Timeout    time.Duration // 连接超时
}

// DefaultConsoleConfig 默认配置
func DefaultConsoleConfig() *ConsoleConfig {
	return &ConsoleConfig{
		DeviceName: "serial0",
		Escape:     '\x1d', // Ctrl+]
		Reconnect:  true,
		Timeout:    10 * time.Second,
	}
}

// NewTerminalConsole 创建终端控制台
func NewTerminalConsole(conn *libvirt.Connect, domainName string, logger *logrus.Logger) (*TerminalConsole, error) {
	if logger == nil {
		logger = logrus.New()
	}

	domain, err := conn.LookupDomainByName(domainName)
	if err != nil {
		return nil, fmt.Errorf("failed to lookup domain: %w", err)
	}

	tc := &TerminalConsole{
		conn:   conn,
		domain: domain,
		logger: logger,
		stopCh: make(chan struct{}),
		errCh:  make(chan error, 2),
		config: DefaultConsoleConfig(),
	}

	return tc, nil
}

// SetConfig 设置配置
func (tc *TerminalConsole) SetConfig(config *ConsoleConfig) {
	tc.mu.Lock()
	defer tc.mu.Unlock()
	tc.config = config
}

// Start 启动终端控制台
func (tc *TerminalConsole) Start(ctx context.Context) error {
	tc.mu.Lock()
	if tc.running {
		tc.mu.Unlock()
		return fmt.Errorf("console already running")
	}
	tc.running = true
	tc.mu.Unlock()

	defer func() {
		tc.mu.Lock()
		tc.running = false
		tc.mu.Unlock()
	}()

	// 检查虚拟机状态
	state, _, err := tc.domain.GetState()
	if err != nil {
		return fmt.Errorf("failed to get domain state: %w", err)
	}
	if state != libvirt.DOMAIN_RUNNING {
		return fmt.Errorf("domain is not running (state: %d)", state)
	}

	// 打印欢迎信息
	tc.printWelcome()

	// 连接到控制台
	if err := tc.connectConsole(); err != nil {
		return fmt.Errorf("failed to connect to console: %w", err)
	}
	defer tc.cleanup()

	// 设置终端为原始模式
	if err := tc.makeRaw(); err != nil {
		return fmt.Errorf("failed to set raw mode: %w", err)
	}
	defer tc.restore()

	// 启动双向数据传输
	go tc.stdinToConsole()
	go tc.consoleToStdout()

	// 等待退出或错误
	select {
	case <-ctx.Done():
		tc.logger.Info("Console interrupted by context")
		return ctx.Err()
	case <-tc.stopCh:
		tc.logger.Info("Console stopped")
		return nil
	case err := <-tc.errCh:
		if err != nil && err != io.EOF {
			tc.logger.WithError(err).Error("Console error")
			return err
		}
		return nil
	}
}

// connectConsole 连接到控制台
func (tc *TerminalConsole) connectConsole() error {
	// 创建 libvirt stream
	stream, err := tc.conn.NewStream(0)
	if err != nil {
		return fmt.Errorf("failed to create stream: %w", err)
	}
	tc.stream = stream

	// 打开控制台
	flags := libvirt.DOMAIN_CONSOLE_FORCE | libvirt.DOMAIN_CONSOLE_SAFE
	err = tc.domain.OpenConsole(tc.config.DeviceName, stream, libvirt.DomainConsoleFlags((flags)))
	if err != nil {
		stream.Free()
		return fmt.Errorf("failed to open console: %w", err)
	}

	tc.logger.WithFields(logrus.Fields{
		"device": tc.config.DeviceName,
	}).Info("Console connected")

	return nil
}

// makeRaw 设置终端为原始模式
func (tc *TerminalConsole) makeRaw() error {
	fd := int(os.Stdin.Fd())

	// 保存当前状态
	oldState, err := term.MakeRaw(fd)
	if err != nil {
		return fmt.Errorf("failed to make raw: %w", err)
	}

	tc.oldState = oldState
	tc.isRaw = true

	tc.logger.Debug("Terminal set to raw mode")
	return nil
}

// restore 恢复终端状态
func (tc *TerminalConsole) restore() {
	if tc.isRaw && tc.oldState != nil {
		fd := int(os.Stdin.Fd())
		if err := term.Restore(fd, tc.oldState); err != nil {
			tc.logger.WithError(err).Error("Failed to restore terminal")
		} else {
			tc.logger.Debug("Terminal restored")
		}
		tc.isRaw = false
	}
}

// stdinToConsole 将标准输入发送到控制台
func (tc *TerminalConsole) stdinToConsole() {
	defer close(tc.stopCh)

	buffer := make([]byte, 1024)

	for {
		select {
		case <-tc.stopCh:
			return
		default:
		}

		n, err := os.Stdin.Read(buffer)
		if err != nil {
			if err != io.EOF {
				tc.errCh <- fmt.Errorf("stdin read error: %w", err)
			}
			return
		}

		if n == 0 {
			continue
		}

		// 检查退出键
		if tc.checkEscape(buffer[:n]) {
			tc.logger.Info("Escape sequence detected, exiting")
			return
		}

		// 发送到虚拟机控制台
		if err := tc.writeToConsole(buffer[:n]); err != nil {
			tc.errCh <- fmt.Errorf("console write error: %w", err)
			return
		}
	}
}

// consoleToStdout 将控制台输出发送到标准输出
func (tc *TerminalConsole) consoleToStdout() {
	buffer := make([]byte, 4096)

	for {
		select {
		case <-tc.stopCh:
			return
		default:
		}

		// 从虚拟机控制台读取
		n, err := tc.readFromConsole(buffer)
		if err != nil {
			if err != io.EOF {
				tc.errCh <- fmt.Errorf("console read error: %w", err)
			}
			return
		}

		if n == 0 {
			continue
		}

		// 写入标准输出
		if _, err := os.Stdout.Write(buffer[:n]); err != nil {
			tc.errCh <- fmt.Errorf("stdout write error: %w", err)
			return
		}

		// 可选：写入日志文件
		if tc.config.LogFile != "" {
			tc.writeToLog(buffer[:n])
		}
	}
}

// writeToConsole 写入控制台
func (tc *TerminalConsole) writeToConsole(data []byte) error {
	if tc.stream == nil {
		return fmt.Errorf("stream not initialized")
	}

	_, err := tc.stream.Send(data)
	if err != nil {
		return fmt.Errorf("stream send failed: %w", err)
	}

	return nil
}

// readFromConsole 从控制台读取
func (tc *TerminalConsole) readFromConsole(buffer []byte) (int, error) {
	if tc.stream == nil {
		return 0, fmt.Errorf("stream not initialized")
	}

	n, err := tc.stream.Recv(buffer)
	if err != nil {
		// libvirt 可能返回特殊错误码
		if e, ok := err.(libvirt.Error); ok {
			if e.Code == libvirt.ERR_OK {
				return n, nil
			}
		}
		return n, err
	}

	return n, nil
}

// checkEscape 检查退出键
func (tc *TerminalConsole) checkEscape(data []byte) bool {
	if tc.config.Escape == 0 {
		return false
	}

	for _, b := range data {
		if rune(b) == tc.config.Escape {
			return true
		}
	}
	return false
}

// writeToLog 写入日志文件
func (tc *TerminalConsole) writeToLog(data []byte) {
	// 实现日志写入（简化版）
	// 生产环境应该使用带缓冲的写入
	if tc.config.LogFile != "" {
		f, err := os.OpenFile(tc.config.LogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			tc.logger.WithError(err).Warn("Failed to open log file")
			return
		}
		defer f.Close()
		f.Write(data)
	}
}

// cleanup 清理资源
func (tc *TerminalConsole) cleanup() {
	tc.logger.Debug("Cleaning up console resources")

	if tc.stream != nil {
		tc.stream.Finish()
		tc.stream.Free()
		tc.stream = nil
	}

	if tc.domain != nil {
		tc.domain.Free()
		tc.domain = nil
	}
}

// Stop 停止控制台
func (tc *TerminalConsole) Stop() {
	tc.mu.Lock()
	defer tc.mu.Unlock()

	if tc.running {
		close(tc.stopCh)
	}
}

// printWelcome 打印欢迎信息
func (tc *TerminalConsole) printWelcome() {
	domainName, _ := tc.domain.GetName()

	fmt.Printf("Connected to domain: %-35s\n", domainName)
	fmt.Println("")
	fmt.Printf("Escape character is %s\n", tc.getEscapeKey())
	fmt.Println("Press Ctrl+] to exit console")
	fmt.Println()
}

// getEscapeKey 获取退出键描述
func (tc *TerminalConsole) getEscapeKey() string {
	switch tc.config.Escape {
	case '\x1d':
		return "Ctrl+]"
	case '\x03':
		return "Ctrl+C"
	case '\x1b':
		return "ESC"
	default:
		return fmt.Sprintf("0x%02x", tc.config.Escape)
	}
}
